#include <cstdint>
#include <cstring>
#include <vector>
#include <string>
#include <iostream>

// =============== Windows ==============
#if defined(_WIN32) || defined(_WIN64)
#include <cstdint>
#include <ws2tcpip.h>
#include <winsock2.h>
#include "ws2ipdef.h"
#include <windows.h>
#pragma comment(lib, "ws2_32.lib")
#define close(sock) closesocket(sock)
typedef SOCKET socket_t;
#define INVALID_SOCKET_VALUE INVALID_SOCKET
using ushort = unsigned short;
// =========  Unix/Linux/Macos  =========
#else
#include <arpa/inet.h>
#include <sys/socket.h>
#include <unistd.h>
#include <ifaddrs.h> //ipv4
#include <netdb.h>   //ipv4
#include <sys/select.h>
#define INVALID_SOCKET_VALUE -1
typedef int             socket_t;
typedef struct sockaddr SOCKADDR;
#endif

using namespace std;

// 地址结构体，业务报文专用
#pragma pack(push, 1)
struct Address {
	uint32_t ip;   // 网络字节序
	uint16_t port; // 网络字节序
};
#pragma pack(pop)

// 命令类型枚举（7位, 最多128个命令类型）
enum MsgCmd : uint8_t {
	SGW_SUMMON    = 0x00, // 网关召唤
	SGW_SYNCHRO   = 0x01, // 服务同步
	SGW_HEARTBEAT = 0x02, // 网关心跳
	API_PING      = 0x10, // Ping
	API_LOGIC     = 0x11, // API接口
};

// magic编码/解码工具
inline uint8_t make_magic(bool is_business, MsgCmd cmd) { return (static_cast<uint8_t>(is_business) << 7) | (cmd & 0x7F); }
inline bool    is_business_magic(uint8_t magic) { return (magic & 0x80) != 0; }
inline MsgCmd  get_cmd_magic(uint8_t magic) { return static_cast<MsgCmd>(magic & 0x7F); }

#pragma pack(push, 1)
struct MessageHeader {
	uint8_t  magic;          // 最高位: 报文类型（0:网关, 1:业务），低7位: 命令类型
	uint8_t  flag;           // 标志位
	uint16_t payload_len;    // 负载长度
	uint16_t fragment_id;    // 分片ID
	uint16_t fragment_count; // 分片总数
};

struct Message {
	MessageHeader        header;
	Address              routers[4]; // 仅业务报文携带, 网关报文忽略
	std::vector<uint8_t> payload;

	Message() {
		memset(&header, 0, sizeof(header));
		memset(routers, 0, sizeof(routers));
	}

	// 判断是否为业务报文
	bool is_business() const { return is_business_magic(header.magic); }
	// 获取命令类型
	MsgCmd cmd_type() const { return get_cmd_magic(header.magic); }

	// 序列化
	std::vector<uint8_t> serialize() const {
		std::vector<uint8_t> data;
		data.insert(data.end(), reinterpret_cast<const uint8_t *>(&header), reinterpret_cast<const uint8_t *>(&header) + sizeof(header));
		if (is_business()) {
			data.insert(data.end(), reinterpret_cast<const uint8_t *>(routers), reinterpret_cast<const uint8_t *>(routers) + sizeof(routers));
		}
		data.insert(data.end(), payload.begin(), payload.end());
		return data;
	}
	// 反序列化
	static Message deserialize(const uint8_t *data, size_t len) {
		Message msg;
		size_t  offset = 0;
		memcpy(&msg.header, data + offset, sizeof(msg.header));
		offset += sizeof(msg.header);
		if (is_business_magic(msg.header.magic)) {
			memcpy(msg.routers, data + offset, sizeof(msg.routers));
			offset += sizeof(msg.routers);
		}
		msg.payload.resize(msg.header.payload_len);
		memcpy(msg.payload.data(), data + offset, msg.header.payload_len);
		return msg;
	}
};
#pragma pack(pop)

// Linux：   g++ -std=c++11 -pthread -o message_v2.exe message_v2.cpp
// Windows： g++ -std=c++11 -pthread -o message_v2.exe message_v2.cpp -lws2_32
int main() {
	Message msg;
	msg.header.magic          = make_magic(false, MsgCmd::API_PING);
	msg.header.flag           = 0;
	msg.header.fragment_id    = 1;
	msg.header.fragment_count = 1;
	msg.header.payload_len    = 11;
	msg.routers[0]            = Address{.ip = 32, .port = 1234};
	string str                = "Hello World";
	// msg.payload               = std::vector<uint8_t>(str.begin(), str.end());

	std::vector<uint8_t> ret = msg.serialize();

	Message msg2;
	msg2 = Message::deserialize(ret.data(), ret.size());
	std::cout << msg2.header.magic << std::endl;
	std::cout << msg2.header.flag << std::endl;
	std::cout << msg2.header.fragment_id << std::endl;
	std::cout << msg2.header.fragment_count << std::endl;
	std::cout << msg2.header.payload_len << std::endl;
	// std::cout << msg2 << std::endl;
	// std::cout << std::string(reinterpret_cast<const char *>(msg2.payload.data(), msg2.payload.size())) << std::endl;

	return 0;
}
